#region Copyright RenGuiYou. All rights reserved.

//=====================================================
// NeatlyFrameWork
// Author:      RenGuiyou
// Feedback: 	mailto:750539605@qq.com
//=====================================================

#endregion

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Serialization;
using UnityEngine.UI;

namespace Neatly.Tween
{
    public class EffectTween : MonoBehaviour
    {
        private static EffectTween m_Instance;

        public static EffectTween Instance
        {
            get
            {
                if (m_Instance==null)
                {
                    Init();
                }
                return m_Instance;
            }
        }

        public static void Init()
        {
            GameObject go = new GameObject("~EffectTween");
            m_Instance = go.AddComponent<EffectTween>();
            DontDestroyOnLoad(go);
        }

        private static uint m_counter = 0;

        private static uint Counter
        {
            get
            {
                m_counter++;
                if (m_counter > uint.MaxValue - 1)
                {
                    m_counter = 1;
                }

                return m_counter;
            }
        }

        public delegate Vector2 PositionDelegate(float y);

        public delegate float AlphaDelegate(float y);

        private readonly List<EffectTweenImp> m_Tweens = new List<EffectTweenImp>();

        [FormerlySerializedAs("TweenRtPosPool")] public EffectTweenObjectPool<EffectTweenRtPosObj> effectTweenRtPosPool = new EffectTweenObjectPool<EffectTweenRtPosObj>();
        public EffectTweenObjectPool<EffectTweenImp> ImplementPool = new EffectTweenObjectPool<EffectTweenImp>();

        private static EffectTweenImp CreateImplement(EffectTweenObjectBase itween)
        {
            EffectTweenImp imp = Instance.ImplementPool.Create().Init(Counter, itween);
            Instance.m_Tweens.Add(imp);
            return imp;
        }

        // Move (World Position)
        public static EffectTweenImp Move(GameObject gameObject, Vector3 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateMove(gameObject, target, time);
            return CreateImplement(itween);
        }

        // Move (LocalPosition)
        public static EffectTweenImp MoveLocal(GameObject gameObject, Vector3 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateMoveLocal(gameObject, target, time);
            return CreateImplement(itween);
        }

        public static EffectTweenImp Move(RectTransform rt, float x, float y, float time)
        {
            return Move(rt, new Vector2(x, y), time);
        }

        // Move (UI)
        public static EffectTweenImp Move(RectTransform rt, Vector2 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateMove(rt, target, time);
            return CreateImplement(itween);
        }

        // Move Bezier (UI)
        public static EffectTweenImp MoveUIBezier(RectTransform rt, Vector2 target, Vector2 bezier, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateBezierMove(rt, target, bezier, time);
            return CreateImplement(itween);
        }

        // Move Bezier (UI)
        public static EffectTweenImp MoveUIBezier(RectTransform rt, RectTransform target, float percent, float y, float time)
        {
            var bezier = NMathf.Lerp(rt.anchoredPosition, target.anchoredPosition, percent);
            bezier.y = y;
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateBezierMove(rt, target.anchoredPosition, bezier, time);
            return CreateImplement(itween);
        }

        // Move Bezier (UI)
        public static EffectTweenImp MoveUIBezier(RectTransform rt, Vector2 target, float percent, float y, float time)
        {
            var bezier = NMathf.Lerp(rt.anchoredPosition, target, percent);
            bezier.y = y;
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateBezierMove(rt, target, bezier, time);
            return CreateImplement(itween);
        }

        // Move X(UI)
        public static EffectTweenImp MoveX(RectTransform rt, float x, float time)
        {
            Vector2 target = new Vector2(x, rt.anchoredPosition.y);
            return Move(rt, target, time);
        }

        // Move Y(UI)
        public static EffectTweenImp MoveY(RectTransform rt, float y, float time)
        {
            Vector2 target = new Vector2(rt.anchoredPosition.x, y);
            return Move(rt, target, time);
        }

        public static EffectTweenImp SizeDeltaX(RectTransform rt, float val, float time)
        {
            Vector2 target = new Vector2(val, rt.sizeDelta.y);
            return SizeDelta(rt, target, time);
        }

        public static EffectTweenImp SizeDeltaY(RectTransform rt, float val, float time)
        {
            Vector2 target = new Vector2(rt.sizeDelta.x, val);
            return SizeDelta(rt, target, time);
        }

        public static EffectTweenImp SizeDelta(RectTransform rt, Vector2 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateSizeDelta(rt, target, time);
            return CreateImplement(itween);
        }

        // Scale
        public static EffectTweenImp Scale(Transform rt, Vector3 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateScale(rt, target, time);
            return CreateImplement(itween);
        }

        public static EffectTweenImp Scale(Transform rt, float scale, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateScale(rt, new Vector3(scale, scale, scale), time);
            return CreateImplement(itween);
        }

        // ScaleY
        public static EffectTweenImp ScaleY(Transform rt, float target, float time)
        {
            Vector3 v = rt.localScale;
            v.y = target;
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateScale(rt, v, time);
            return CreateImplement(itween);
        }

        // Rotate
        public static EffectTweenImp Rotate(Transform rt, Vector3 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateRotate(rt, target, time);
            return CreateImplement(itween);
        }

        public static EffectTweenImp RotateX(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.x = angle;
            return Rotate(rt, euler, time);
        }

        public static EffectTweenImp RotateY(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.y = angle;
            return Rotate(rt, euler, time);
        }

        public static EffectTweenImp RotateZ(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.z = angle;
            return Rotate(rt, euler, time);
        }

        public static EffectTweenImp AutoRotate(Transform rt, Vector3 target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateAutoRotate(rt, target, time);
            return CreateImplement(itween);
        }

        public static EffectTweenImp AutoRotateX(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.x = angle;
            return AutoRotate(rt, euler, time);
        }

        public static EffectTweenImp AutoRotateY(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.y = angle;
            return AutoRotate(rt, euler, time);
        }

        public static EffectTweenImp AutoRotateZ(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.z = angle;
            return AutoRotate(rt, euler, time);
        }
        
        // Graphic Color
        public static EffectTweenImp Color(Graphic graphic, Color target, float time)
        {
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateColor(graphic, target, time);
            return CreateImplement(itween);
        }

        // Graphic Color
        public static EffectTweenImp ColorAlpha(Graphic graphic, float target, float time)
        {
            Color c = graphic.color;
            c.a = target;
            EffectTweenObjectBase itween = EffectTweenRtPosObj.CreateColor(graphic, c, time);
            return CreateImplement(itween);
        }
        
        void Update()
        {
            for (int i = m_Tweens.Count - 1; i >= 0; i--)
            {
                if (m_Tweens[i].IsDestroy)
                {
                    m_Tweens[i].Destroy();
                    m_Tweens.RemoveAt(i);
                    continue;
                }

                m_Tweens[i].Excute(Time.deltaTime);
            }
        }

        public static void Cancel(uint id)
        {
            if (id <= 0) return;
            for (int i = 0; i < Instance.m_Tweens.Count; i++)
            {
                if (Instance.m_Tweens[i].id == id)
                {
                    Instance.m_Tweens[i].Cancel();
                }
            }
        }

        public static void Cancel(GameObject gameObject)
        {
            if (Instance == null || gameObject == null) return;
            for (int i = 0; i < Instance.m_Tweens.Count; i++)
            {
                if (Instance.m_Tweens[i].gameObject == gameObject)
                {
                    Instance.m_Tweens[i].Cancel();
                }
            }
        }

        public static void Cancel(Component cmp)
        {
            if (Instance == null || cmp == null) return;
            for (int i = 0; i < Instance.m_Tweens.Count; i++)
            {
                if (Instance.m_Tweens[i].gameObject == cmp.gameObject)
                {
                    Instance.m_Tweens[i].Cancel();
                }
            }
        }

        public static void Pause(uint id)
        {
            for (int i = 0; i < Instance.m_Tweens.Count; i++)
            {
                if (Instance.m_Tweens[i].id == id)
                {
                    Instance.m_Tweens[i].Pause();
                }
            }
        }

        public static void Resume(uint id)
        {
            for (int i = 0; i < Instance.m_Tweens.Count; i++)
            {
                if (Instance.m_Tweens[i].id == id)
                {
                    Instance.m_Tweens[i].Resume();
                }
            }
        }
    }
}